package com.peaksport.framework.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.peaksport.framework.base.entity.CoreBaseEntity;
import com.peaksport.framework.cache.SimpleMemoryCache;
import com.peaksport.framework.exception.PKDevException;
import com.peaksport.framework.extend.jpa.PKJpaRepositoryImpl;
import com.peaksport.framework.extend.redis.annotation.PKCacheEvict;
import com.peaksport.framework.extend.redis.annotation.PKCachePut;
import com.peaksport.framework.extend.redis.annotation.PKCacheable;
import com.peaksport.framework.extend.spring.PKApplicationContext;
import com.peaksport.framework.map.NoCaseMap;

/**
 * 
 *
 * @author jinbin_guo
 *
 */
public class PKClassUtils {
	
	/** 本地内存缓存 */
	private static SimpleMemoryCache localCache = null;
	
	static {
		localCache = SimpleMemoryCache.getInstance();
	}
	
    /**
     * 是否继承 CoreBaseEntity
     * @param clazz
     * @return
     */
    public static boolean isExtendBaseEntity(Class<?> clazz) {
        return isAssignableFrom(CoreBaseEntity.class,clazz);
    }
	
    /**
     * 是否继承关系
     * @param parentClass
     * @param subClass
     * @return
     */
	public static boolean isAssignableFrom(Class<?> parentClass, Class<?> subClass) {
        if( parentClass == null || subClass == null) return false;
        return parentClass.isAssignableFrom(subClass);
    }
	
	
	/**
	 * 
	 * 获取Class类的全部字段,包含子类、私有的字段(使用本地内存缓存)
	 * map.key class类字段名,不区分大小写
	 * 区别于getTableField
	 * @param clazz
	 * @return
	 */
	
	@SuppressWarnings("unchecked")
	public static NoCaseMap<Field> getAllField(Class<?> clazz) {
		String keyLocalCache = "PKClassUtils.getAllField(" + clazz.getName() + ")";
		NoCaseMap<Field> mapField = (NoCaseMap<Field>) localCache.getCache(keyLocalCache);
		if (mapField != null) return mapField;
		mapField = new NoCaseMap<Field>();
		while (clazz != null) {
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				String fieldName = field.getName();
				if (mapField.get(fieldName) != null) continue;
				mapField.put(fieldName, field);
			}
			clazz = clazz.getSuperclass();
		}
		localCache.putCache(keyLocalCache, mapField);
		return mapField;
		 
	} 
	
	
	/**
	 * 
	 * 获取Class类的全部注解字段,包含子类、私有的数据库字段名称(使用本地内存缓存)
	 * map.key class类注解字段的表字段名,不区分大小写
	 * 区别于getAllField
	 * @param classz
	 * @return
	 */
	
	@SuppressWarnings("unchecked")
	public static NoCaseMap<Field> getAllTableField(Class<?> clazz) {
		String keyLocalCache = "PKClassUtils.getAllTableField(" + clazz.getName() + ")";
		NoCaseMap<Field> mapFieldByTableName = (NoCaseMap<Field>) localCache.getCache(keyLocalCache);
		if (mapFieldByTableName != null) return mapFieldByTableName;
		mapFieldByTableName = new NoCaseMap<Field>();
		NoCaseMap<Field> mapAllField = getAllField(clazz);
		Set<String> setKey = mapAllField.keySet();
		Iterator<String> itKey = setKey.iterator();
		while (itKey.hasNext()) {
			String key = itKey.next();
			Field field = mapAllField.get(key);
			String fieldByTableName = getTableFieldName(field);
			if (fieldByTableName != null) {
				if (mapFieldByTableName.get(fieldByTableName) == null) 
					mapFieldByTableName.put(fieldByTableName, field);
			}
		}
		localCache.putCache(keyLocalCache, mapFieldByTableName);
		return mapFieldByTableName;
	}
	
	 /**
     * 获取class类的所有字段名称
     * map.key class字段属性名称,不区分大小写
     * @param clazz
     * @return
     */
	@SuppressWarnings("unchecked")
	public static NoCaseMap<String> getAllFieldName(Class<?> clazz) {
		String keyLocalCache = "PKClassUtils.getAllFieldName(" + clazz.getName() + ")";
		NoCaseMap<String> mapFieldMapping = (NoCaseMap<String>) localCache.getCache(keyLocalCache);
		if (mapFieldMapping != null) return mapFieldMapping;
        mapFieldMapping = new NoCaseMap<String>();
        while (clazz != null) {
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; fields != null && i < fields.length; i ++) {
                String fieldName = fields[i].getName();
                mapFieldMapping.put(fieldName,fieldName);
            }
            clazz = clazz.getSuperclass();
        }
        localCache.putCache(keyLocalCache, mapFieldMapping);
        return mapFieldMapping;
    }


	/**
     * 获取实体类中注解的表名
     * @param entityClass
     * @return
     */
	public static String getTableName(Class<?> entityClass) {
        if (entityClass == null) return null;
        Annotation ann = entityClass.getAnnotation(Table.class);
        if (ann == null) return null;
        return ((Table)ann).name();
    }

    /**
     * 获取实体对象中注解字段的表字段名
     * @param entityField
     * @param checkExists 检查注解是否必须存在
     * @return
     */
    @SuppressWarnings("rawtypes")
	public static String getTableFieldName(Field entityField, boolean checkExists) {
    	 Annotation ann = entityField.getAnnotation(Column.class);
         if (ann != null) return ((Column)ann).name();
         ann = entityField.getAnnotation(JoinColumn.class);
         if (ann != null) return ((JoinColumn)ann).name();
         if (checkExists && entityField.getAnnotation(Transient.class) == null) {
        	 String className = ((Class) PKInvokeUtils.getFieldValue(entityField, "clazz")).getName();
        	// String className = entityField.getClass().getName();
        	 String fieldName = entityField.getName();
        	 throw new PKDevException(String.format("实体对象[%s]属性[%s]不存在注解[Column/JoinColumn]",className,fieldName));
         }
         else return null;
    }
    /**
     * 获取实体对象中注解字段的表字段名
     * @param entityField
     * @return
     */
    public static String getTableFieldName(Field entityField) {
    	return getTableFieldName(entityField,true);
    }
    
    /**
     * 获取切片缓存的有效期类型
     * @param method
     * @return
     */
    public static String getCacheExpireTimeType(Method method) {
    	Annotation ann = method.getAnnotation(PKCacheable.class);
    	if (ann != null) return ((PKCacheable)ann).expireTimeType();
    	ann = method.getAnnotation(PKCacheEvict.class);
    	if (ann != null) return ((PKCacheEvict)ann).expireTimeType();
    	ann = method.getAnnotation(PKCachePut.class);
    	if (ann != null) return ((PKCachePut)ann).expireTimeType();
    	return null;
    }
    
    /**
     * 获取对象的field
     * @param owner
     * @param fieldName
     * @return
     */
    public static Field getField(Object owner,String fieldName) {
    	if (owner == null) throw PKDevException.throwException("PKClassUtils.getField(owner,fieldName),owner不能为空");
    	if (fieldName == null) throw PKDevException.throwException("PKClassUtils.getField(owner,fieldName),fieldName不能为空");
    	Class<?> cls = owner.getClass();
    	NoCaseMap<Field> mapAllField = getAllField(cls);
		Field field = mapAllField.get(fieldName);
		if (field == null) {
			throw new PKDevException(String.format("对象%s不存在属性%s",owner.getClass().getName(),fieldName));
		}
		field.setAccessible(true);
		return field;

	}
    
    /**
     *  获取实体对象中注解字段的转化器
     * @param entityField
     * @return
     */
    
	public static Class<?> getFieldConverter(Field entityField) {
		if (entityField == null) throw PKDevException.throwException("PKClassUtils.getFieldConverter(entityField),entityField不能为空");
        Annotation ann = entityField.getAnnotation(Convert.class);
        if (ann == null) return null;
        return ((Convert)ann).converter();
    }
	

    /**
     * 字段类型
     * @param clazz
     * @param fieldName
     * @return
     */
	public static Class<?> getFieldType(Class<?> clazz,String fieldName) {
        Field field = getField(clazz,fieldName);
        return field.getType();
    }
	
	/**
     * 获取实体对象属性的泛型类型
     * @param field
     * @return
     */
    public static Class<?> getGenericClass(Field field) {
    	if (field == null) throw PKDevException.throwException("PKClassUtils.getGenericClass(field),field不能为空");
        if (!List.class.isAssignableFrom(field.getType()))  throw PKDevException.throwException("实体对象[%s]的泛型属性[%s]必须使用List集合",PKInvokeUtils.getFieldValue(field,"clazz"),field.getName());
        String genericTypeName = field.getGenericType().getTypeName();
        String typeName = genericTypeName.replace(field.getType().getName(),"").replace("<","").replace(">","");
        Class<?> clazz = null;
        try {
            clazz = getClass(typeName);
            return clazz;
        } catch (Exception exc) {
            throw PKDevException.throwException("实体对象的泛型属性[%s]异常",field.getName());
        }
    }
    
    /**
	 * 
	 * 获取Class类的全部方法,包含子类、私有的字段(使用本地内存缓存)
	 * map.key class类字段名,不区分大小写
	 * @param clazz
	 * @return
	 */
	
	@SuppressWarnings("unchecked")
	public static NoCaseMap<Method> getAllMethod(Class<?> clazz) {
		String keyLocalCache = "PKClassUtils.getAllMethod(" + clazz.getName() + ")";
		NoCaseMap<Method> mapMethod = (NoCaseMap<Method>) localCache.getCache(keyLocalCache);
		if (mapMethod != null) return mapMethod;
		mapMethod = new NoCaseMap<Method>();
		while (clazz != null) {
			Method[] methods = clazz.getDeclaredMethods();
			for (Method method : methods) {
				String methodName = method.getName();
				Class<?>[] parametersTypes = method.getParameterTypes();
				StringBuffer allMethodName = new StringBuffer(methodName);
				for (int i = 0; parametersTypes != null && i < parametersTypes.length; i++) {
					if (i == 0) allMethodName.append("(");
					allMethodName.append(parametersTypes[i].getName());
					if (i != parametersTypes.length -1) allMethodName.append(",");
					if (i == parametersTypes.length -1) allMethodName.append(")");
				}
				if (mapMethod.get(allMethodName) != null) continue;
				mapMethod.put(allMethodName.toString(),method);
			}
			clazz = clazz.getSuperclass();
		}
		localCache.putCache(keyLocalCache, mapMethod);
		return mapMethod;
	} 
	
	/**
	 * 获取class类的某个方法
	 * @param clazz
	 * @param methodName
	 * @param paramTypes
	 * @return
	 */
	public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
		NoCaseMap<Method> mapMethod = getAllMethod(clazz);
		StringBuffer allMethodName = new StringBuffer(methodName);
		for (int i = 0; paramTypes != null && i < paramTypes.length; i++) {
			if (i == 0) allMethodName.append("(");
			allMethodName.append(paramTypes[i].getName());
			if (i != paramTypes.length -1) allMethodName.append(",");
			if (i == paramTypes.length -1) allMethodName.append(")");
		}
		Method method = mapMethod.get(allMethodName);
		if (method == null) throw PKDevException.throwException("class类[%s]不存在方法[%s]", clazz.getName(),allMethodName);
		return method;
	}
	
	/**
	 *  获取方法的返回类型
	 * @param method
	 * @return
	 */
	public static Class<?> getMethodReturnType(Method method) {
		return method.getReturnType();
	}
	
	/**
	 *  获取方法的返回泛型类型
	 * @param method
	 * @return
	 */
	public static Class<?> getMethodGenericReturnType(Method method) {
		String genericTypeName = method.getGenericReturnType().getTypeName();
        String typeName = genericTypeName.replace(method.getReturnType().getName(),"").replace("<","").replace(">","");
        Class<?> clazz = null;
        try {
            clazz = getClass(typeName);
            return clazz;
        } catch (Exception exc) {
        	String methodName = method.getName();
        	Class<?> [] paramTypes = method.getParameterTypes();
        	StringBuffer allMethodName = new StringBuffer(methodName);
    		for (int i = 0; paramTypes != null && i < paramTypes.length; i++) {
    			if (i == 0) allMethodName.append("(");
    			allMethodName.append(paramTypes[i].getName());
    			if (i != paramTypes.length -1) allMethodName.append(",");
    			if (i == paramTypes.length -1) allMethodName.append(")");
    		}
            throw PKDevException.throwException("class类[%s]方法[%s]返回类型不是有效的泛型", clazz.getName(),allMethodName);
        }
	}
    
    /**
     * 获取类
     * @param className
     * @return
     */
    public static Class<?> getClass(String className) {
        try {
            return Class.forName(className);
        } catch (Exception exc) {
            throw new PKDevException(exc);
        }
    }
    
    /**
     * 获取上下文实例化的bean
     * @param clazz
     * @return
     */
    public static Object getBean(Class<?> clazz) {
    	return PKApplicationContext.getBean(clazz);
    }
    
    /**
     * 获取实类型获取实体类
     * @param entityType
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
	public static Class getEntityClass(String strEntityType) {
    	if (PKPublicUtils.isEmpty(strEntityType)) throw PKDevException.throwException("PKClassUtils.getEntityClass(strEntityType)-->strEntityType不能为空.");
		NoCaseMap<Class> mapClass = (NoCaseMap<Class>) localCache.getCache(PKJpaRepositoryImpl.cache_key_forever_entityType);
		Class clazz = mapClass.get(strEntityType);
		return clazz;
    }
	
}
